package com.sinszm.sofa;

import cn.hutool.core.lang.Assert;
import com.sinszm.sofa.exception.ApiException;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Jedis缓存操作
 * <p>
 *     常用操作
 * </p>
 * @author fh411
 */
@Component
public class JedisUtil<T extends Serializable> {

    @Resource
    private ObjectRedisTemplate<T> tObjectRedisTemplate;

    @Resource
    private RedissonClient redissonClient;

    /**
     * redisson
     *
     * @return {RedissonClient}
     */
    public RedissonClient redisson() {
        return redissonClient;
    }

    /**
     * VALUE模式
     *
     * @return 模式
     */
    public ValueOperations<String, T> value() {
        return tObjectRedisTemplate.opsForValue();
    }

    /**
     * HASH模式
     *
     * @return 模式
     */
    public HashOperations<String, String, T> hash() {
        return tObjectRedisTemplate.opsForHash();
    }

    /**
     * SET模式
     *
     * @return 模式
     */
    public SetOperations<String, T> set() {
        return tObjectRedisTemplate.opsForSet();
    }

    /**
     * ZSET模式
     *
     * @return 模式
     */
    public ZSetOperations<String, T> zSet() {
        return tObjectRedisTemplate.opsForZSet();
    }

    /**
     * LIST模式
     *
     * @return 模式
     */
    public ListOperations<String, T> list() {
        return tObjectRedisTemplate.opsForList();
    }

    /**
     * STREAM模式
     *
     * @return 模式
     */
    public StreamOperations<String, String, T> stream() {
        return tObjectRedisTemplate.opsForStream();
    }

    /**
     * 基本
     * <p>
     *     仅限：value模式
     * </p>
     * @param key   键
     * @param data  数据
     */
    public void set(String key, T data) {
        Assert.notEmpty(key, () -> new ApiException("-1", "键不能为空"));
        Assert.notNull(data, () -> new ApiException("-1", "数据不能为空"));
        value().set(key, data);
    }

    /**
     * 基本
     * <p>
     *     仅限：value模式
     * </p>
     * @param key       键
     * @param data      数据
     * @param expire    过期时间，精度：秒
     */
    public void set(String key, T data, long expire) {
        set(key, data);
        if (expire >= 0) {
            expire(key, expire);
        }
    }

    /**
     * 获取数据
     * <p>
     *     仅限：value模式
     * </p>
     * @param key   键
     * @return      数据
     */
    public T get(String key) {
        Assert.notEmpty(key, () -> new ApiException("-1", "键不能为空"));
        return Optional.ofNullable(value().get(key)).orElse(null);
    }

    /**
     * 查询缓存剩余生成时间
     * <p>
     *     仅限：value模式
     * </p>
     * @param key   键
     * @return      剩余过期时间，精度：秒
     */
    public Long ttl(String key) {
        return value().getOperations().getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 设置过期
     *
     * @param key       键
     * @param expire    过期时间，精度：秒
     * @return          结果
     */
    public Boolean expire(String key, long expire) {
        Assert.notEmpty(key, () -> new ApiException("-1", "键不能为空"));
        Boolean result = tObjectRedisTemplate.expire(key, expire, TimeUnit.SECONDS);
        return result != null && result;
    }

    /**
     * 判断整个缓存键是否存在
     * @param key   键
     * @return      结果，true存在，false不存在
     */
    public Boolean exists(String key) {
        Assert.notEmpty(key, () -> new ApiException("-1", "键不能为空"));
        Boolean result = tObjectRedisTemplate.hasKey(key);
        return result != null && result;
    }

    /**
     * 删除整个缓存键
     * @param key   键
     * @return      删除成功的个数
     */
    public Long del(String... key) {
        return tObjectRedisTemplate.delete(Arrays.asList(key));
    }

    /**
     * 清空所有缓存键
     */
    public Long flushDb() {
        Set<String> keys = tObjectRedisTemplate.keys("*");
        if (keys != null && keys.size() > 0) {
            return del(keys.toArray(new String[0]));
        }
        return 0L;
    }

}
